#!/bin/bash

#
# findsymbol -- by Dario Berzano <dario.berzano@gmail.com>
#
# Part of the lfscripts suite -- http://lfscripts.googecode.com/
#
# Looks for a symbol (or part of it) in a list of libraries. When found prints
# out the matching library plus a list of matching symbols.
#
# User can specify whether print libraries where symbol is actually defined or
# also libraries referencing the symbol.
#

#
# Global variables
#

export Prog=`basename "$0"`

#
# Functions
#

# Prints usage
function PrintHelp() {
  cat <<_EOF_
$Prog -- by Dario Berzano <dario.berzano@cern.ch>
Prints libraries matching a specified symbol name, or part of it.

Usage: $Prog [options] [libraries...]
    -h, --help                       this help screen
    -a, --all                        match also references to the symbol
    -s, --symbol NAME                symbol name
_EOF_
}

# Main function
function Main() {

  local Args SymbolName Lib LinkTarget Tmp AllSymbols

  # By default look only for _defined_ symbols (T, t)
  AllSymbols=0

  # A decent version of getopt is required!
  Prog=`basename "$0"`
  Args=`getopt -o 's:ah' \
    --long 'symbol:,all,help' -n "$Prog" -- "$@"`
  [ $? != 0 ] && exit 1
  eval set -- "$Args"

  while [ "$1" != '--' ] ; do

    case "$1" in

      -s|--symbol)
        SymbolName="$2"
        shift 2
      ;;

      -a|--all)
        AllSymbols=1
        shift 1
      ;;

      --help|-h)
        PrintHelp
        return 1
      ;;

    esac

  done

  shift # --
  #echo "Remaining: $@"

  # Check for parameters
  if [ "$SymbolName" == '' ] ; then
    echo 'Please specify a symbol name (or part of it) via --symbol|-s.' >&2
    return 1
  fi

  # Store all libraries with full path into a file: symbolic links are resolved
  Tmp=`mktemp /tmp/$Prog-XXXXX`

  for Lib in "$@" ; do

    if [ -L "$Lib" ] ; then
      # Resolve symlink
      LinkTarget=`readlink "$Lib"`
      if [[ "$LinkTarget" != /* ]] ; then
        # Path is relative: prepend path of lib
        Lib=`dirname "$Lib"`/"$LinkTarget"
      else
        Lib="$LinkTarget"
      fi
    fi

    # Skip if it is not a file
    [ ! -f "$Lib" ] && continue

    # Normalize path
    Lib=`echo "$Lib" | perl -e "use Cwd 'realpath'; print realpath(<>);"`

    # Print out
    echo $Lib

  done | sort -u > $Tmp  # Remove duplicates

  # Find symbol in libraries
  TmpGrep1=`mktemp /tmp/$Prog-XXXXX`
  TmpGrep2=`mktemp /tmp/$Prog-XXXXX`
  TmpGrep3=`mktemp /tmp/$Prog-XXXXX`
  while read Lib ; do

    # Launch nm to get the list of symbols
    nm "$Lib" | grep -- "$SymbolName" > $TmpGrep1 || continue

    # Symbol matches
    if [ $AllSymbols == 0 ] ; then
      cat $TmpGrep1 | egrep ' t | T ' > $TmpGrep2 || continue
    else
      cat $TmpGrep1 > $TmpGrep2
    fi

    # List all symbols matching expression
    while read Line ; do
      PerlExpr="/([A-Za-z]) ([^\s]*$SymbolName[^\s]*)/ "
      PerlExpr="${PerlExpr} and print \"\$2(\033[36m\$1\033[m)\\n\""
      echo $Line | perl -ne "$PerlExpr"
    done < <(cat $TmpGrep2) > $TmpGrep3

    # Now cat
    if [ -s $TmpGrep3 ] ; then
      echo -en "\033[1m$Lib\033[m : "
      echo $(cat $TmpGrep3) | grep --color "$SymbolName"
    fi

  done < <(cat $Tmp)

  # Cleanup
  rm -f $Tmp $TmpGrep1 $TmpGrep2 $TmpGrep3

}

#
# Entry point
#

Main "$@" || exit $?
